/*******************************************************************************
 * Copyright (c) 2001, 2002 Boris Pruessmann and others. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Common Public License v0.5 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.ui.action;

import com.google.common.collect.Lists;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.api.IChangeResult;
import net.sourceforge.perforce.core.api.IChangesResult;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.internal.P4TeamProvider;
import net.sourceforge.perforce.core.resources.IChangelist;
import net.sourceforge.perforce.ui.dialogs.ConfirmationDialog;
import net.sourceforge.perforce.ui.internal.Policy;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.team.core.RepositoryProvider;

import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @version $Revision: 488 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class RevertUnchangedAction extends PerforceAction {
  //----------------------------------------------------------------------------
  // -- Public Methods

  @Override
  public void run(IAction action) {
    run(new IRunnableWithProgress() {
      public void run(IProgressMonitor monitor) throws InvocationTargetException {
        final IChangelist[] changelists = getSelectedChangelists();
        Policy.assertTrue(changelists.length == 1);

        Integer changelist = changelists[0].getId();
        try {
          IPerforceServer server = changelists[0].getServer();

          final String[] files = server.diff(getFilesOfChangelist(server, changelist));
          if (files.length == 0) {
            final String title = Policy.bind("RevertUnchangedAction.dialogTitle"); //$NON-NLS-1$
            final String message = Policy.bind("RevertUnchangedAction.noFilesDialogMessage"); //$NON-NLS-1$

            getShell().getDisplay().syncExec(new Runnable() {
              public void run() {
                MessageDialog.openInformation(getShell(), title, message);
              }
            });

            return;
          }

          final boolean[] doRevert = {false};
          getShell().getDisplay().syncExec(new Runnable() {
            public void run() {
              String title = Policy.bind("RevertUnchangedAction.dialogTitle"); //$NON-NLS-1$
              String message = Policy.bind("RevertUnchangedAction.dialogMessage"); //$NON-NLS-1$

              doRevert[0] = ConfirmationDialog.openConfirmation(getShell(), title, message, files);
            }
          });

          if (doRevert[0]) {
            server.revert(files, null);

            IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();

            IFile[] fileResources = new IFile[files.length];
            for (int i = 0, length = files.length; i < length; i++) {
              fileResources[i] = root.getFileForLocation(new Path(files[i]));
            }
            refreshState(fileResources, monitor);

            PerforceCorePlugin.getPlugin().broadcastChangelistStateChanges(changelists);
          }
        } catch (PerforceException e) {
          throw new InvocationTargetException(e);
        }
      }
    }, PROGRESS_DIALOG);
  }

  // ---------------------------------------------------------------------------
  // Protected Methods

  @Override
  protected boolean isEnabled() {
    IChangelist[] changelists = getSelectedChangelists();
    if (changelists.length != 1) {
      return false;
    }

    for (int i = 0; i < changelists.length; i++) {
      IPerforceServer server = changelists[i].getServer();
      if (!(server.getClient().equals(changelists[i].getClientName()))) {
        return false;
      }

      if (changelists[i].getFiles().length == 0) {
        return false;
      }
    }

    return true;
  }

  //----------------------------------------------------------------------------
  // - Private Methods

  /**
   * Returns an array of files that belong to the given (pending) changelist.
   * 
   * @param server reference to the perforce server.
   * @param changelist the changelist id.
   * @return array of files
   * 
   * @throws PerforceException in case of an exception.
   */
  private String[] getFilesOfChangelist(IPerforceServer server, Integer changelist)
      throws PerforceException {
    IChangeResult changeResult = server.change(changelist);

    Map<String, String> specDataMap = changeResult.getChangeSpecData().getSpecDataMap();
    List<String> files = Lists.newArrayList();
    for (Map.Entry<String, String> entry : specDataMap.entrySet()) {
      if (entry.getKey().startsWith("Files")) //$NON-NLS-1$
      {
        files.add(entry.getValue());
      }
    }

    return files.toArray(new String[files.size()]);
  }

  /**
   * Returns an array of all selected IChangesResult objects.
   * 
   * @return array of selected...
   */
  private IChangelist[] getSelectedChangelists() {
    List<IChangelist> resources = null;
    if (!getSelection().isEmpty()) {
      resources = Lists.newArrayList();
      for (Iterator<?> elements = getSelection().iterator(); elements.hasNext();) {
        Object next = elements.next();

        if (next instanceof IChangelist) {
          resources.add((IChangelist) next);
          continue;
        } else if (next instanceof IAdaptable) {
          IAdaptable a = (IAdaptable) next;
          Object adapter = a.getAdapter(IChangelist.class);
          if (adapter instanceof IChangesResult) {
            resources.add((IChangelist) adapter);
            continue;
          }
        }
      }
    }

    if (resources != null && !resources.isEmpty()) {
      return resources.toArray(new IChangelist[resources.size()]);
    }

    return new IChangelist[0];
  }

  /**
   * Call this method to retrieve the local state of the given files.
   * 
   * @param files array of files to refresh.
   * @param monitor the progress monitor.
   */
  private void refreshState(IFile[] files, IProgressMonitor monitor) {
    if (files[0] != null) {
      P4TeamProvider provider =
          (P4TeamProvider) RepositoryProvider.getProvider(files[0].getProject());
      provider.refreshState(files, IResource.DEPTH_ZERO, monitor);
    }
  }
}
